package de.tabuma.shopping.logik;

import de.tabuma.logging.entity.*;
import de.tabuma.logging.logik.*;

import java.util.*;
import java.util.logging.*;

import java.lang.reflect.*;

import javax.jdo.*;
import javax.jdo.annotations.PrimaryKey;
import javax.jdo.annotations.Persistent;

/**
  * @author $Author: $
  * @version $Revision: $
  * @since $Date: $
 */
public class PMF {

    private static final Logger log = Logger.getLogger(PMF.class.getName());
    private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("shopping-transaction");
    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }

    /**
     * Method to persist an entity object
     * @param <T> Class of Entity Object
     * @param object Object that will be persisted
     * @return Id of the created object
     */
    public static <T> long doCreate(T object, String user) {
        Long id = new Long(0);
        try {
            PersistenceManager pm = PMF.get().getPersistenceManager();
            pm.makePersistent(object);
            if(log.getLevel()==Level.FINE || log.getLevel()==Level.FINEST) {
                log.fine("create object "+ObjectMethods.toString(object));
            }
            ArrayList<LogField> logFields = new ArrayList<LogField>();
            LogEntry logEntry = null;
            for(int i=0;i<object.getClass().getDeclaredFields().length;i++) {
                Field field = object.getClass().getDeclaredFields()[i];
                PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
                log.finest("field "+field.getName()+" has annotation:" + primaryKey);
                if(primaryKey!=null) {
                    id = (Long) object.getClass().getMethod(ObjectMethods.getGetter(field.getName())).invoke(object);
                    //i = object.getClass().getFields().length;
                    logEntry = new LogEntry();
                    logEntry.setClassName(object.getClass().getName());
                    logEntry.setUser(user);
                    logEntry.setObjectId(id);
                }
                else {
                    Persistent persistent = field.getAnnotation(Persistent.class);
                    if(persistent!=null) {
                        LogField logField = new LogField();
                        logField.setFieldName(field.getName());
                        Method method = object.getClass().getMethod(ObjectMethods.getGetter(field.getName()));

                        if(method==null) {
                            method = object.getClass().getMethod(ObjectMethods.getIs(field.getName()));
                        }
                        Object obj = method.invoke(object);
                        if(obj==null) {
                            logField.setFieldValue("null");
                        }
                        else {
                            logField.setFieldValue(obj.toString());
                        }
                        logFields.add(logField);
                    }

                }
            }
            if(logEntry!=null) {
                int logEntryId = LogEntryFacade.create(logEntry);
                for(int i=0;i<logFields.size();i++) {
                    LogField logField = logFields.get(i);
                    logField.setLogEntryId(logEntryId);
                    LogFieldFacade.create(logField);
                }
            }

            pm.close();
        }
        catch(Exception e) {
            String a = "";
            try { a = ObjectMethods.toString(object); } catch(Exception e1) {}
            log.warning(e.getMessage() + "/" + e.getCause() + "/" + e.getClass() + " at " + object + "(" + a + ")");
            String warning = "";
            for(int i=0;i<e.getStackTrace().length;i++) {
                warning += e.getStackTrace()[i]+"\n";
            }
            log.warning(warning);
        }
        return id;
    }

    /**
     * Method
     * @param <T> Class of Entity Object
     * @param object Object that will be persisted
     * @return Id of the created object
     */
    public static <T> void doRemove(T object, String user) {
        Long id = new Long(0);
        try {
            PersistenceManager pm = PMF.get().getPersistenceManager();
            for(int i=0;i<object.getClass().getDeclaredFields().length;i++) {
                Field field = object.getClass().getDeclaredFields()[i];
                PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
                log.finest("field "+field.getName()+" has annotation:" + primaryKey);
                if(primaryKey!=null) {
                    id = (Long) object.getClass().getMethod(ObjectMethods.getGetter(field.getName())).invoke(object);
                    //i = object.getClass().getFields().length;
                    LogEntry logEntry = new LogEntry();
                    logEntry.setClassName(object.getClass().getName());
                    logEntry.setUser(user);
                    logEntry.setObjectId(id);
                    logEntry.setRemoveObject(true);
                    LogEntryFacade.create(logEntry);
                    Query query = pm.newQuery(object.getClass());
                    query.setFilter(field.getName() + " == " + id);
                    List<T> list = (List<T>)query.execute();
                    if(list.size()>0) {
                        object = list.get(0);
                    }

                }
            }
            pm.deletePersistent(object);
            pm.close();
        }
        catch(Exception e) {
            String a = "";
            try { a = ObjectMethods.toString(object); } catch(Exception e1) {}
            log.warning(e.getMessage() + "/" + e.getCause() + "/" + e.getClass() + " at " + object + "(" + a + ")");
            String warning = "";
            for(int i=0;i<e.getStackTrace().length;i++) {
                warning += e.getStackTrace()[i]+"\n";
            }
            log.warning(warning);
        }
    }

    /**
     * Method get object.
     * @param <T> Class of entity object
     * @param id ID
     * @return Object
     */
    public static <T> T get(long id, T object) {
        try {

            PersistenceManager pm = PMF.get().getPersistenceManager();
            Query query = pm.newQuery(object.getClass());
            String fieldName = "id";
            for(int i=0;i<object.getClass().getDeclaredFields().length;i++) {
                Field field = object.getClass().getDeclaredFields()[i];
                PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
                log.finest("field "+field.getName()+" has annotation:" + primaryKey);
                if(primaryKey!=null) {
                    fieldName = field.getName();
                    i = object.getClass().getFields().length;
                }
            }

            query.setFilter(fieldName + " == " + id);
            List<T> list = (List<T>)query.execute();
            if(list.size()>0) {
                object = list.get(0);
            }
            query.closeAll();
            pm.close();
            if(log.getLevel()==Level.FINE || log.getLevel()==Level.FINEST) {
                log.fine("id:"+id+";"+ObjectMethods.toString(object));
            }
        }
        catch(Exception e) {
            log.warning(e.getMessage() + "/" + e.getCause() + "/" + e.getClass() + " at "+object);
            String warning = "";
            for(int i=0;i<e.getStackTrace().length;i++) {
                warning += e.getStackTrace()[i]+"\n";
            }
            log.warning(warning);
        }
        return object;
    }

    /**
     * Method get object.
     * @param <T> Class of entity object
     * @param key list of keys that will be used as filter
     * @param value list of values that will be used as filter
     * @return Object
     */
    public static <T> List<T> get(String key[], String value[],T object) {
        try {
            PersistenceManager pm = PMF.get().getPersistenceManager();
            Query query = pm.newQuery(object.getClass());
            
            String filter = "";
            if(key!=null && value!=null) {
                if(key.length==value.length) {
                    for(int i=0;i<key.length;i++) {

                        if(i!=0) {
                            filter += " && ";
                        }

                        boolean quote = false;
                        try {
                            Integer.valueOf(value[i]);
                        }
                        catch(Exception e) {
                            quote = true;
                        }

                        if(quote) {
                            try {
                                quote = false;
                                Double.valueOf(value[i]);
                            }
                            catch(Exception e) {
                                quote = true;
                            }
                        }
                        if(!quote) {
                            filter += key[i]+"=="+value[i];
                        }
                        else {
                            filter += key[i]+"=='"+value[i]+"'";
                        }

                    }
                }
            }
            query.setFilter(filter);
            List<T> list = (List<T>)query.execute();
            ArrayList<T> list1 = new ArrayList<T>();

            for(int i=0;i<list.size();i++) {
                list1.add(list.get(i));
            }
            query.closeAll();
            pm.close();
            
            if(log.getLevel()==Level.FINE || log.getLevel()==Level.FINEST) {
                log.fine("filter:"+filter+";"+ObjectMethods.toString(object));
            }
            return list1;
        }
        catch(Exception e) {
            log.warning(e.getMessage() + "/" + e.getCause() + "/" + e.getClass() + " at "+object);
            String warning = "";
            for(int i=0;i<e.getStackTrace().length;i++) {
                warning += e.getStackTrace()[i]+"\n";
            }
            log.warning(warning);
        }
        return null;
    }

    /**
     * Method to update object.
     * @param <T> Class of entity object
     */
    public static <T> void doUpdate(T object, String user) {
        Long id = null;
        String fieldName = "id";
        try {
            log.fine(object.toString());
            //Finding id
            LogEntry logEntry = null;
            ArrayList<LogField> logFields = new ArrayList<LogField>();
            for(int i=0;i<object.getClass().getDeclaredFields().length;i++) {
                Field field = object.getClass().getDeclaredFields()[i];
                PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
                if(primaryKey!=null) {
                    log.fine("field "+field.getName()+" has annotation:" + primaryKey);
                    id = (Long) object.getClass().getMethod(ObjectMethods.getGetter(field.getName())).invoke(object);
                    fieldName = field.getName();
                    //i = object.getClass().getFields().length;
                    logEntry = new LogEntry();
                    logEntry.setClassName(object.getClass().getName());
                    logEntry.setUser(user);
                    logEntry.setObjectId(id);
                }
                else {
                    Persistent persistent = field.getAnnotation(Persistent.class);
                    if(persistent!=null) {
                        LogField logField = new LogField();
                        Method method = object.getClass().getMethod(ObjectMethods.getGetter(field.getName()));
                        if(method==null) {
                            method = object.getClass().getMethod(ObjectMethods.getIs(field.getName()));
                        }
                        Object obj = method.invoke(object);
                        if(obj==null) {
                            logField.setFieldValue("null");
                        }
                        else {
                            logField.setFieldValue(obj.toString());
                        }
                        logFields.add(logField);
                    }
                }
            }
            PersistenceManager pm = PMF.get().getPersistenceManager();
            Query query = pm.newQuery(object.getClass());
            query.setFilter(fieldName + " == " + id);
            List<T> list = (List<T>)query.execute();
            if(list.size()>0) {
                T object1 = list.get(0);
                ObjectCopyFacade.copy(object,object1);
                if(log.getLevel()==Level.FINE || log.getLevel()==Level.FINEST) {
                    String logText = "obj1:"+ObjectMethods.toString(object);
                    log.fine(logText);
                }

                if(logEntry!=null) {
                    int logEntryId = LogEntryFacade.create(logEntry);
                    for(int i=0;i<logFields.size();i++) {
                        LogField logField = logFields.get(i);
                        logField.setLogEntryId(logEntryId);
                        LogFieldFacade.create(logField);
                    }
                }

            }
            else {
                log.warning(object + " width " + fieldName + " = " + id + " has no values.");
                doCreate(object, user);
            }
            query.closeAll();
            pm.close();
        }
        catch(Exception e) {
            log.warning(e.getMessage()+ "/" + e.getCause() + "/" + e.getClass() + " at "+object+"(id="+id);
            String warning = "";
            for(int i=0;i<e.getStackTrace().length;i++) {
                warning += e.getStackTrace()[i]+"\n";
            }
            log.warning(warning);
        }
    }
}
